package es.efor.ics.gev.dao.jpa;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;

import javax.persistence.NoResultException;

import org.springframework.stereotype.Repository;

import es.efor.ics.gev.dao.EventoDao;
import es.efor.ics.gev.model.Accion;
import es.efor.ics.gev.model.AtributoAccion;
import es.efor.ics.gev.model.AtributoFase;
import es.efor.ics.gev.model.AtributoInformado;
import es.efor.ics.gev.model.AtributoMaestro;
import es.efor.ics.gev.model.AtributoTipologia;
import es.efor.ics.gev.model.Documento;
import es.efor.ics.gev.model.EstadoAccion;
import es.efor.ics.gev.model.EstadoFase;
import es.efor.ics.gev.model.Evento;
import es.efor.ics.gev.model.Fase;
import es.efor.ics.gev.model.GenericAtributoInformado;
import es.efor.ics.gev.model.ModalidadEvento;
import es.efor.ics.gev.model.ModalidadImparticionEvento;
import es.efor.ics.gev.model.Perfil;
import es.efor.ics.gev.model.Persona;
import es.efor.ics.gev.model.TipoEvento;

/**
 * This class interacts with EntityManager to save/delete and retrieve
 * <code>Evento</code> objects.
 * 
 * @author dlisbona
 */
@Repository("eventoDao")
public class EventoDaoJpa extends GenericDaoJpa<Evento, Long> implements
		EventoDao {

	/**
	 * Constructor that sets the entity to Evento.class.
	 */
	public EventoDaoJpa() {
		super(Evento.class);
	}

	/**
	 * {@inheritDoc}
	 */
	public Evento getEventoByNombre(String nombreEvento) {
		String value = nombreEvento.toUpperCase();
		try {
			return (Evento) getEntityManager().createNamedQuery(
					"evento.findEventoByNombre").setParameter("nombre", value)
					.getSingleResult();
		} catch (NoResultException e) {
			return null;
		}
	}

	/**
	 * {@inheritDoc}
	 */
	public Evento getEventoByCodigo(String codigoEvento) {
		String value = codigoEvento.toUpperCase();
		try {
			return (Evento) getEntityManager().createNamedQuery(
					"evento.findEventoByCodigo").setParameter("codigo", value)
					.getSingleResult();
		} catch (NoResultException e) {
			return null;
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<Evento> getEventos() {
		return getEntityManager().createNamedQuery("evento.findAllEventos")
				.getResultList();
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<Evento> getEventosByNombreDepartamento(String nombreDepartamento) {
		String value = '%' + nombreDepartamento.toUpperCase() + '%';
		return getEntityManager().createNamedQuery(
				"evento.findEventosByNombreDepartamento")
				.setParameter("nombre", value).getResultList();
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<Evento> getEventosByCodigo(String codigo) {
		String value = '%' + codigo.toUpperCase() + '%';
		return getEntityManager().createNamedQuery("evento.findEventosByCodigo")
				.setParameter("codigo", value).getResultList();
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<Evento> getEventosByTipologia(Long tipologiaId) {
		return getEntityManager().createNamedQuery("evento.findEventosByTipologia")
				.setParameter("id", tipologiaId).getResultList();
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<Evento> getEventosByModalidad(ModalidadEvento modalidad) {
		return getEntityManager().createNamedQuery("evento.findEventosByModalidad")
				.setParameter("modalidad", modalidad).getResultList();
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<Evento> getEventosByNombre(String nombre) {
		String value = '%' + nombre.toUpperCase() + '%';
		return getEntityManager().createNamedQuery("evento.findEventosByNombre")
				.setParameter("nombre", value).getResultList();
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<Evento> getEventosByTipologiaAndModalidad(Long tipologiaId,
			ModalidadEvento modalidad) {
		return getEntityManager().createNamedQuery(
				"evento.findEventosByTipologiaAndModalidad").setParameter("id",
				tipologiaId).setParameter("modalidad", modalidad)
				.getResultList();
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<Evento> getEventosByTipologiaAndNombre(Long tipologiaId,
			String nombre) {
		String value = nombre.toUpperCase() + '%';
		return getEntityManager().createNamedQuery(
				"evento.findEventosByTipologiaAndNombre").setParameter("id",
				tipologiaId).setParameter("nombre", value).getResultList();
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<Evento> getEventosByTipologiaAndModalidadAndNombre(
			Long tipologiaId, ModalidadEvento modalidad, String nombre) {
		String value = nombre.toUpperCase() + '%';
		return getEntityManager().createNamedQuery(
				"evento.findEventosByTipologiaAndModalidadAndNombre").setParameter(
				"id", tipologiaId).setParameter("modalidad", modalidad)
				.setParameter("nombre", value).getResultList();
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<Evento> getEventosByModalidadAndNombre(
			ModalidadEvento modalidad, String nombre) {
		String value = nombre.toUpperCase() + '%';
		return getEntityManager().createNamedQuery(
				"evento.findEventosByModalidadAndNombre").setParameter("modalidad",
				modalidad).setParameter("nombre", value).getResultList();
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<AtributoMaestro> getAtributos(Long eventoId) {
		return getEntityManager().createNamedQuery("evento.findAtributosEvento")
				.setParameter("id", eventoId).getResultList();
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<Fase> getAllFases(Long eventoId) {
		return getEntityManager().createNamedQuery("evento.findAllFasesEvento")
				.setParameter("id", eventoId).getResultList();
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<Fase> getFasesAtemporales(Long eventoId) {
		return getEntityManager()
				.createNamedQuery("evento.findFasesAtemporalesEvento").setParameter(
						"id", eventoId).getResultList();
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<Fase> getFasesTemporales(Long eventoId) {
		return getEntityManager().createNamedQuery("evento.findFasesTemporalesEvento")
				.setParameter("id", eventoId).getResultList();
	}

	/**
	 * {@inheritDoc}
	 */
	public Evento createEvento(String nombreEvento, TipoEvento tipoEvento,
			ModalidadEvento modalidad,
			ModalidadImparticionEvento modalidadImparticion,
			Date fechaInicio,
			Date fechaFin) {
		Evento evento = tipoEvento.createEvento(nombreEvento, modalidad,
				modalidadImparticion, fechaInicio, fechaFin);
		getEntityManager().flush();

		for (AtributoMaestro atributo : tipoEvento.getAtributos()) {
			evento.addAtributoInformado(atributo, atributo.getValorDefecto());
		}
		for (Fase fase : tipoEvento.getFases()) {
			evento.addValorEstadoFase(fase, fase.getEstadoInicial());
			for (AtributoMaestro atributo : fase.getAtributos()) {
				evento.addAtributoFaseInformado(fase, atributo, atributo
						.getValorDefecto());
			}
			for (Accion accion : fase.getAcciones()) {
				evento.addValorEstadoAccion(fase, accion, accion
						.getEstadoInicial());
				for (AtributoMaestro atributo : accion.getAtributos()) {
					evento.addAtributoAccionInformado(fase, accion, atributo,
							atributo.getValorDefecto());
				}
			}
		}
		getEntityManager().flush();
		return evento;
	}

	/**
	 * {@inheritDoc}
	 */
	public Evento saveEvento(Evento evento) {
		Evento e = getEntityManager().merge(evento);
		getEntityManager().flush();
		return e;
	}

	@Override
	public Evento save(Evento evento) {
		return saveEvento(evento);
	}

	/**
	 * {@inheritDoc}
	 */
	public Evento setValorAtributoEvento(Evento evento,
			AtributoMaestro atributo, String valor) {
		if (evento.isAtributoInformado(atributo.getId())) {
			evento.getAtributoInformado(atributo.getId()).setValor(valor);
		} else {
			evento.addAtributoInformado(atributo, valor);
		}
		getEntityManager().flush();
		return evento;
	}

	/**
	 * {@inheritDoc}
	 */
	public SortedMap<AtributoTipologia, String> getMapAtributosEvento(
			Evento evento) {
		SortedMap<AtributoTipologia, String> result = new TreeMap<AtributoTipologia, String>();
		for (AtributoTipologia a : evento.getTipoEvento()
				.getAtributosTipologia()) {
			result.put(a, evento.getValorAtributoInformado(a.getId()
					.getAtributoId()));
		}
		return result;
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<AtributoInformado> getValoresAtributosEvento(Evento evento) {
		List<AtributoInformado> result = new ArrayList<AtributoInformado>();
		for (AtributoTipologia a : evento.getTipoEvento()
				.getAtributosTipologia()) {
			result.add(new GenericAtributoInformado(a, evento
					.getValorAtributoInformado(a.getId().getAtributoId())));
		}
		return result;
	}

	/**
	 * {@inheritDoc}
	 */
	public Evento setValorAtributoFase(Evento evento, Fase fase,
			AtributoMaestro atributo, String valor) {
		if (evento.isAtributoFaseInformado(fase.getId(), atributo.getId())) {
			evento.getAtributoFaseInformado(fase.getId(), atributo.getId())
					.setValor(valor);
		} else {
			evento.addAtributoFaseInformado(fase, atributo, valor);
		}
		getEntityManager().flush();
		return evento;
	}

	/**
	 * {@inheritDoc}
	 */
	public SortedMap<AtributoFase, String> getMapAtributosFase(Evento evento,
			Fase fase) {
		SortedMap<AtributoFase, String> result = new TreeMap<AtributoFase, String>();
		for (AtributoFase a : fase.getAtributosFase()) {
			result.put(a, evento.getValorAtributoFaseInformado(fase.getId(), a
					.getId().getAtributoId()));
		}
		return result;
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<AtributoInformado> getValoresAtributosFase(Evento evento,
			Fase fase) {
		List<AtributoInformado> result = new ArrayList<AtributoInformado>();
		for (AtributoFase a : fase.getAtributosFase()) {
			result.add(new GenericAtributoInformado(a, evento
					.getValorAtributoFaseInformado(fase.getId(), a.getId()
							.getAtributoId())));
		}
		return result;
	}

	/**
	 * {@inheritDoc}
	 */
	public Evento setValorAtributoAccion(Evento evento, Fase fase,
			Accion accion, AtributoMaestro atributo, String valor) {
		if (evento.isAtributoAccionInformado(fase.getId(), accion.getId(),
				atributo.getId())) {
			evento.getAtributoAccionInformado(fase.getId(), accion.getId(),
					atributo.getId()).setValor(valor);
		} else {
			evento.addAtributoAccionInformado(fase, accion, atributo, valor);
		}
		getEntityManager().flush();
		return evento;
	}

	/**
	 * {@inheritDoc}
	 */
	public SortedMap<AtributoAccion, String> getMapAtributosAccion(
			Evento evento, Fase fase, Accion accion) {
		SortedMap<AtributoAccion, String> result = new TreeMap<AtributoAccion, String>();
		for (AtributoAccion a : accion.getAtributosAccion()) {
			result.put(a, evento.getValorAtributoAccionInformado(fase.getId(),
					accion.getId(), a.getId().getAtributoId()));
		}
		return result;
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<AtributoInformado> getValoresAtributosAccion(Evento evento,
			Fase fase, Accion accion) {
		List<AtributoInformado> result = new ArrayList<AtributoInformado>();
		for (AtributoAccion a : accion.getAtributosAccion()) {
			result.add(new GenericAtributoInformado(a, evento
					.getValorAtributoAccionInformado(fase.getId(), accion
							.getId(), a.getId().getAtributoId())));
		}
		return result;
	}

	/**
	 * {@inheritDoc}
	 */
	public Evento setEstadoFaseEvento(Evento evento, Fase fase,
			EstadoFase estado) {
		if (evento.isValorEstadoFaseAsociado(fase.getId())) {
			evento.getValorEstadoFase(fase.getId()).setEstadoFase(estado);
		} else {
			evento.addValorEstadoFase(fase, estado);
		}
		getEntityManager().flush();
		return evento;
	}

	/**
	 * {@inheritDoc}
	 */
	public Evento setEstadoAccionEvento(Evento evento, Fase fase,
			Accion accion, EstadoAccion estado) {
		if (evento.isValorEstadoAccionAsociado(fase.getId(), accion.getId())) {
			evento.getValorEstadoAccion(fase.getId(), accion.getId())
					.setEstadoAccion(estado);
		} else {
			evento.addValorEstadoAccion(fase, accion, estado);
		}
		getEntityManager().flush();
		return evento;
	}

	/**
	 * {@inheritDoc}
	 */
	public Documento addDocumentoToFase(Evento evento, Fase fase,
			Documento documento) {
		evento.addDocumentoFase(fase, documento);
		getEntityManager().flush();
		return evento.getDocumentoFase(fase.getId(), documento.getId())
				.getDocumento();
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<Persona> getPersonasWithPerfil(Long eventoId, Long perfilId) {
		return getEntityManager().createNamedQuery(
				"evento.findPersonasWithPerfil").setParameter("eventoId",
				eventoId).setParameter("perfilId", perfilId).getResultList();
	}

}
